#include<stdio.h>

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/timers.h"
#include "freertos/queue.h"
#include "driver/gpio.h"
#include "esp_system.h"

#include "button.h"

static int button_start_stop_callback(void * para1, void * para2);
static int button1_callback(void * para1, void * para2);

static TaskHandle_t button_task; // 任务1结构对象
static TimerHandle_t button_timer;
static QueueHandle_t button_queue;
static const char *TAG = "button";
static const custom_button_t custom_button_array[] = {
    {19, ButtonStartStop, button_start_stop_callback},
    {21, Button1, button1_callback},
};

static void configure_gpio(int gpio_num)
{
    gpio_config_t gpio_conf;
    gpio_conf.intr_type = GPIO_INTR_DISABLE;
    gpio_conf.mode = GPIO_MODE_INPUT;
    gpio_conf.pin_bit_mask = (1ULL << gpio_num);
    gpio_conf.pull_down_en = GPIO_PULLDOWN_ENABLE;// active level is 1.
    gpio_conf.pull_up_en = GPIO_PULLUP_DISABLE; 

    gpio_config(&gpio_conf);
    printf("%s: init gpio[%d]\r\n",TAG, gpio_num);
    
}

/**
 * 获取哪一个按键按下
 */
static button_id_t get_pressed_button_id(void) {
    uint8_t state;
    button_id_t ret = ButtonNone;
    for (int i=0; i < (sizeof(custom_button_array) / sizeof(custom_button_t));i++) {
        state = gpio_get_level(custom_button_array[i].button_gpio_num);
        if (state == 1) {
            ret = custom_button_array[i].button_id;
            break;
        }
    }

    return ret;
}

static void button_timer_callback(TimerHandle_t timer)
{
    static button_id_t prePressedButton = ButtonNone;
    static enum {
        NO_DOWN,
        RECHECK_DOWN,
        CONFIRMED_DOWN,
        RECHECK_UP
    } scanState = NO_DOWN;

    // 读取当前硬件状态
    button_id_t currPressedButton = get_pressed_button_id();

    switch (scanState) {
        case NO_DOWN:
            // 有按键按下，进入第1次确认检查状态
            if (currPressedButton != ButtonNone) {
                scanState = RECHECK_DOWN;
                DEBUG_PRINT("NO_DOWN -> RECHECK_DOWN:%d\n", currPressedButton);
            }
            break;
        case RECHECK_DOWN:
            if (currPressedButton == ButtonNone) {
                scanState = NO_DOWN;
                DEBUG_PRINT("RECHECK_DOWN -> NO_DOWN:%d\n", currPressedButton);
            } else if (prePressedButton == currPressedButton) {
                // 发消息通知，根据按键类型，决定如何处理. 可以考虑从某个配置表中查，这样可配置多个按键事件的通知行为
                if(currPressedButton == ButtonStartStop) {
                    if (xQueueSendToFront(button_queue, (void *)&currPressedButton, 0) != pdTRUE) {
                        printf("%s: failed send\n", TAG);
                    }
                } else {
                    if (xQueueSendToBack(button_queue, (void *)&currPressedButton, 0) != pdTRUE) {
                        printf("%s: failed send\n", TAG);
                    }
                }

                scanState = CONFIRMED_DOWN;
                DEBUG_PRINT("RECHECK_DOWN -> CONFIRMED_DOWN:%d\n", currPressedButton);
            }
            break;
        case CONFIRMED_DOWN:
            // 进入这个状态后，要做的是检查按键是否重复按下或者等待按键释.这里简单起见，等待按键释放
            if (currPressedButton == ButtonNone) {
                // 发现按键释放？进一步确认。但这里要return，否则prePressedButton会在后面设置None
                scanState = RECHECK_UP;
                DEBUG_PRINT("CONFIRMED_DOWN -> RECHECK_UP:%d\n", currPressedButton);
                return;
            } else if (currPressedButton != prePressedButton) {
                // 发现不一样的按键按下，重新确认
                scanState = RECHECK_DOWN;
                DEBUG_PRINT("CONFIRMED_DOWN -> RECHECK_DOWN:%d\n", currPressedButton);
            }
            break;
        case RECHECK_UP:
            if (currPressedButton == ButtonNone) {
                // 确认没有按键按下，已经释放
                scanState = NO_DOWN;
                DEBUG_PRINT("RECHECK_UP -> NO_DOWN:%d\n", currPressedButton);
            } else if (currPressedButton != prePressedButton) {
                // 发现不一样的按键按下，重新确认是否按下
                scanState = RECHECK_DOWN;
                DEBUG_PRINT("RECHECK_UP -> scanState:%d\n", currPressedButton);
            } else if (currPressedButton == prePressedButton) {
                // 相同的键，噢，前一次可能是误触发，再次重新检查
                scanState = CONFIRMED_DOWN;
                DEBUG_PRINT("RECHECK_UP -> CONFIRMED_DOWN:%d\n", currPressedButton);
            }
            break;
    }

    // 记录当前结果
    prePressedButton = currPressedButton;
}

/**
 * 等待任意按键按下
 * @param id 按键是否按下的状态
 * @return 非0，有错误发生；0无错误
 */
uint32_t ButtonWaitPress (button_id_t * id) {
    uint32_t err = 0;

    err = xQueueReceive(button_queue, (void **)id, portMAX_DELAY);
    if(!err) {
        printf("queue recv err=%d\n", err);
    }
    return err;
}

void button_taskEntry (void * param) {
    button_id_t buttonId;

    for (;;) {
        ButtonWaitPress(&buttonId);
        custom_button_array[buttonId - BUTTON_ID_OFFSET].button_cb(NULL, NULL);
    }
}

/**
 * 按键初始化
 */
void ButtonInit (void) {
    for(int i=0; i< (sizeof(custom_button_array) / sizeof(custom_button_t)); i++) {
        configure_gpio(custom_button_array[i].button_gpio_num);
    }

    button_queue = xQueueCreate(10, sizeof(int));

    xTaskCreate(&button_taskEntry, "button_task", 1024*2, NULL, configMAX_PRIORITIES-10, &button_task);

    button_timer = xTimerCreate("button_timer", 20 / portTICK_PERIOD_MS, pdTRUE,
                                         NULL, button_timer_callback);

    xTimerStart(button_timer, 1);
}

static int button_start_stop_callback(void * para1, void * para2)
{
    for (int i = 3; i >= 0; i--) {
        printf("Restarting in %d seconds...\n", i);
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
    printf("Restarting now.\n");
    fflush(stdout);
    esp_restart();
}

static int button1_callback(void * para1, void * para2)
{
    printf("button1 pressed.\n");
    return 0;
}